package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.tian.common.CommonResult;
import com.tian.dto.ChargeUserLoginResDto;
import com.tian.dto.user.UserCouponAddReqDto;
import com.tian.entity.ChargeCoupon;
import com.tian.entity.ChargeUserPoint;
import com.tian.enums.CouponSendTypeEnum;
import com.tian.message.MessageReqIdPrefixConstant;
import com.tian.message.UserGetCouponMessage;
import com.tian.producer.UserGetCouponProducer;
import com.tian.service.ChargeUserCouponSecondsKillService;
import com.tian.util.ChargeMarketRedisKeyPrefix;
import com.tian.util.DateUtils;
import com.tian.util.UserCacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * {@code @description:} 抢购优惠券服务实现类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-08-16 22:06
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ChargeUserCouponSecondsKillServiceImpl implements ChargeUserCouponSecondsKillService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private UserGetCouponProducer userGetCouponProducer;
    private static final Set<Integer> LOCAL_COUPON_CACHE_SET = new ConcurrentSkipListSet<>();

    @Override
    public CommonResult<Boolean> secondsKill(UserCouponAddReqDto userCouponAddReqDto) {
        ChargeUserLoginResDto user = UserCacheUtil.getUser();
        Long userId = user.getId();
        Integer couponId = userCouponAddReqDto.getCouponId();
        if (LOCAL_COUPON_CACHE_SET.contains(couponId)) {
            log.info("用户id:{}，优惠券id:{}，优惠券已抢购过", userId, couponId);
            return CommonResult.failed("优惠券已抢购完了，感谢参与");
        }
        RBucket<String> couponBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.CHARGE_COUPON_KEY + userCouponAddReqDto.getCouponId());
        ChargeCoupon chargeCoupon = JSON.parseObject(couponBucket.get(), ChargeCoupon.class);
        if (chargeCoupon == null) {
            log.info("优惠券id:{}，优惠券不存在", couponId);
            return CommonResult.failed("优惠券不存在");
        }
        if (chargeCoupon.getSendType() == CouponSendTypeEnum.POINT.getSendType()) {
            Integer needPoint = chargeCoupon.getPoint();
            RBucket<String> userPointBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.USER_POINT_KEY + userCouponAddReqDto.getCouponId());
            ChargeUserPoint chargeUserPoint = JSON.parseObject(userPointBucket.get(), ChargeUserPoint.class);
            if (chargeUserPoint.getPoint() < needPoint) {
                log.info("用户id:{}，优惠券id:{}，用户积分不足", user.getId(), couponId);
                return CommonResult.failed("您的积分不足！");
            }
        }
        if (chargeCoupon.getValidityType() == 1) {
            if (chargeCoupon.getValidityEndTime().before(new Date())) {
                log.info("用户id:{}，优惠券id:{}，优惠券过期时间计算失败", userId, couponId);
                return CommonResult.failed("优惠券已过期，不可以领取！");
            }
        }

        Integer limit = chargeCoupon.getLimit();
        if (limit == 1) {
            RAtomicLong secondKillBucket = redissonClient.getAtomicLong(ChargeMarketRedisKeyPrefix.USER_SECONDS_KILL_COUPON_KEY + user.getId() + ":" + userCouponAddReqDto.getCouponId());
            if (chargeCoupon.getLimitTimes() <= secondKillBucket.get()) {
                log.info("用户id:{}，优惠券id:{}，用户抢购次数超过限制", user.getId(), couponId);
                return CommonResult.failed("您已经抢购过");
            }
        }

        String luaScript = "if redis.call('lpop', KEYS[1]) then return 1 else return 0 end";
        List<Object> keys = new ArrayList<>();
        keys.add(ChargeMarketRedisKeyPrefix.CHARGE_COUPON_STOCK_QUANTITY_KEY + userCouponAddReqDto.getCouponId());
        Long result = redissonClient.getScript()
                .eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.INTEGER, keys);

        if (result == 1) {
            UserGetCouponMessage userGetCouponMessage = new UserGetCouponMessage();
            userGetCouponMessage.setCouponId(userCouponAddReqDto.getCouponId());
            userGetCouponMessage.setUserId(user.getId());
            userGetCouponMessage.setReqId(MessageReqIdPrefixConstant.USER_GET_COUPON_REQ_ID_PREFIX
                    + UUID.randomUUID() + DateUtils.formatDefaultDateMs());
            userGetCouponProducer.sendUserGetCoupon(userGetCouponMessage);
            return CommonResult.success();
        }
        LOCAL_COUPON_CACHE_SET.add(couponId);
        log.info("用户id:{}，优惠券id:{}，优惠券抢购失败", user.getId(), couponId);
        return CommonResult.success();
    }
}
